﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace CPPEI.WorkflowCore
{
    public class Engine
    {
        /// <summary>
        /// 工作流存储
        /// </summary>
        public static IWorkflowStorage WorkflowStorage { get; private set; }

        /// <summary>
        /// 工作流实例存储
        /// </summary>
        public static IInstanceStorage InstanceStorage { get; private set; }

        public static IActionRecordStorage ActionRecordStorage { get; private set; }

        /// <summary>
        /// 查询服务
        /// </summary>
        public static IQueryService QueryService { get; private set; }

        /// <summary>
        /// 配置工作流引擎
        /// </summary>
        /// <param name="workflowStorage">工作流存储</param>
        /// <param name="instanceStorage">工作流实例存储</param>
        /// <param name="actionRecordStorage">操作记录存储</param>
        /// <exception cref="WFConfigException">配置出现错误</exception>
        public static void Config(IWorkflowStorage workflowStorage, IInstanceStorage instanceStorage,
            IActionRecordStorage actionRecordStorage)
        {
            if(WorkflowStorage != null)
            {
                throw new WFConfigException("工作流引擎已经初始化，不能重复初始化");
            }
            WorkflowStorage = workflowStorage;
            InstanceStorage = instanceStorage;
            ActionRecordStorage = actionRecordStorage;
        }
        
        /// <summary>
        /// 获取流程实例
        /// </summary>
        /// <param name="instanceId"></param>
        /// <returns></returns>
        public static Instance Get(Guid instanceId)
        {
            return InstanceStorage.Find(instanceId);
        }

        /// <summary>
        /// 启动子流程实例
        /// </summary>
        /// <param name="parentInstanceId"></param>
        /// <param name="workflowCode"></param>
        /// <param name="actor"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static Instance Start(Guid parentInstanceId, string workflowCode, Actor actor, object data)
        {
            return null;
        }

        /// <summary>
        /// 启动流程，根据启动最新的流程
        /// </summary>
        /// <param name="workflowCode">工作流编号</param>
        /// <param name="actor">参与者，启动流程的人</param>
        /// <param name="data">流程数据</param>
        /// <returns></returns>
        public static Instance Start(string workflowCode, Actor actor, object data)
        {
            Workflow workflow = WorkflowStorage.Find(workflowCode);
            if(workflow == null)
            {
                throw new WFNotFoundException(workflowCode);
            }

            // 创建实例
            Instance instance = new Instance(workflow);

            // 指定流程的状态。
            State state = new State(workflow.Root);
            instance.State = state;
            instance.State.Actors.Add(actor);
            instance.Data = data;

            // 存储实例
            InstanceStorage.Save(instance);

            // 生成操作记录
            ActionRecord actionRecord = new ActionRecord(instance.Id,
                string.Empty,
                ActionType.Begin,
                null,
                actor,
                UndertakerType.Self);
            ActionRecordStorage.Save(actionRecord);
            
            return instance;
        }

        /// <summary>
        /// 运行工作流
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="stateId"></param>
        /// <param name="action"></param>
        /// <param name="actor"></param>
        /// <param name="target"></param>
        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, Actor target)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            ActorCollection targets = new ActorCollection();
            targets.Add(target);

            Option option = new Option();
            option.UndertakerType = UndertakerType.Self;

            return _run(instance, stateId, action, actor, targets, instance.Data, option);
        }

        /// <summary>
        /// 运行工作流
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="stateId"></param>
        /// <param name="action"></param>
        /// <param name="actor"></param>
        /// <param name="target"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, Actor target, object data)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            ActorCollection targets = new ActorCollection();
            targets.Add(target);

            Option option = new Option();
            option.UndertakerType = UndertakerType.Self;

            return _run(instance, stateId, action, actor, targets, data, option);
        }

        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, Actor target, object data, Option option)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            ActorCollection targets = new ActorCollection();
            targets.Add(target);
            return _run(instance, stateId, action, actor, targets, data, option);
        }

        /// <summary>
        /// 运行工作流
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="stateId"></param>
        /// <param name="action"></param>
        /// <param name="actor"></param>
        /// <param name="targets"></param>
        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, ActorCollection targets)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            Option option = new Option();
            option.UndertakerType = UndertakerType.Self;

            return _run(instance, stateId, action, actor, targets, instance.Data, option);
        }

        /// <summary>
        /// 运行工作流
        /// </summary>
        /// <param name="instanceId"></param>
        /// <param name="stateId"></param>
        /// <param name="action"></param>
        /// <param name="actor"></param>
        /// <param name="target"></param>
        /// <param name="data"></param>
        /// <returns></returns>
        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, ActorCollection targets, object data)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            Option option = new Option();
            option.UndertakerType = UndertakerType.Self;

            return _run(instance, stateId, action, actor, targets, data, option);
        }

        public static Instance Run(Guid instanceId, Guid stateId, string action, Actor actor, ActorCollection targets, object data, Option option)
        {
            Instance instance = InstanceStorage.Find(instanceId);
            if (instance == null)
            {
                throw new InstanceNotFoundException(instanceId);
            }

            return _run(instance, stateId, action, actor, targets, data, option);
        }

        private static Instance _run(Instance instance, Guid stateId, string action, Actor actor, ActorCollection targets, object data, Option option)
        {
            if (instance.State.Id != stateId)
            {
                throw new InvalidStateException(instance.Id);
            }

            Link link = null;
            foreach (var item in instance.State.Activity.Next)
            {
                if (string.Equals(item.Action.Code, action))
                {
                    link = item;
                    break;
                }
            }

            if (link == null)
            {
                throw new ActionInvalidException(instance.Id, action);
            }

            // 保存流转记录
            ActionRecord record = new ActionRecord(instance.Id,
                action,
                ActionType.Submit,
                instance.State.Activity,
                actor,
                option.UndertakerType);
            ActionRecordStorage.Save(record);

            if(link.To.Type == ActivityType.End)
            {
                #region // 流程执行完毕
                State newState = new State(link.To);
                instance.State = newState;
                instance.FinishedTime = DateTime.Now;
                instance.IsFinished = true;
                for (int index = 0; index < targets.Count; index++)
                {
                    instance.State.Actors.Add(targets[index]);
                }

                // 生成流程结束记录
                ActionRecord finishedRecord = new ActionRecord(instance.Id,
                    string.Empty,
                    ActionType.End,
                    instance.State.Activity,
                    actor,
                    option.UndertakerType);
                ActionRecordStorage.Save(finishedRecord);
                #endregion
            }
            else
            {
                // 指定流程的状态
                State newState = new State(link.To);
                instance.State = newState;
                for (int index = 0; index < targets.Count; index++)
                {
                    instance.State.Actors.Add(targets[index]);
                }
            }
            instance.Data = data;

            // 存储实例
            InstanceStorage.Save(instance);

            return instance;
        }
    }
}
